#include "proc/cpu.h"
#include "mem/vmem.h"
#include "mem/pmem.h"
#include "mem/mmap.h"
#include "lib/str.h"
#include "lib/print.h"
#include "dev/timer.h"
#include "syscall/sysfunc.h"
#include "syscall/syscall.h"
#include "memlayout.h"
#include "fs/bitmap.h"
#include "fs/buf.h"
#include "fs/fs.h"
#include "proc/elf.h"
#include "fs/dir.h"
#include "fs/inode.h"
// 堆伸缩
// uint64 new_heap_top 新的堆顶 (如果是0代表查询, 返回旧的堆顶)
// 成功返回新的堆顶 失败返回-1
uint64 sys_brk()
{

    struct proc *p = myproc();       // 获取当前进程
    uint64 new_heap_top;              // 新的堆顶地址
    int addr = p->heap_top;           // 当前堆顶地址

    // 读取用户传入的新堆顶地址
    // 如果失败，返回 -1 表示参数获取失败
    arg_uint64(0, &new_heap_top);

    // 情况 1：查询当前堆顶地址
    // 如果 new_heap_top 为 0，直接返回当前的堆顶地址
    if (new_heap_top == 0){
        return addr;
    }
      
    // 情况 2：增加堆空间
    // 如果新堆顶地址大于当前堆顶，调用 uvm_heap_grow 增加堆空间
    else if (new_heap_top > addr)
    {
        uint64 increment = new_heap_top - addr;   // 计算需要增加的字节数
        p->heap_top = uvm_heap_grow_lazy(p->pgtbl, addr, increment,PTE_R | PTE_W | PTE_U);
        if (p->heap_top != new_heap_top)          // 检查是否成功扩展到目标堆顶
            return -1;
    }

    // 情况 3：减少堆空间
    // 如果新堆顶地址小于当前堆顶，调用 uvm_heap_ungrow 减少堆空间
    else
    {
        uint64 decrement = addr - new_heap_top;  // 计算需要减少的字节数
        p->heap_top = uvm_heap_ungrow(p->pgtbl, addr, decrement);
    }

    // 返回新的堆顶地址
    return p->heap_top;

}

// 内存映射
// uint64 start 起始地址 (如果为0则由内核自主选择一个合适的起点, 通常是顺序扫描找到一个够大的空闲空间)
// uint32 len   范围(字节, 检查是否是page-aligned)
// 成功返回映射空间的起始地址, 失败返回-1
uint64 sys_mmap()
{

    uint64 start;
    uint32 len;

    // 读取参数：start (起始地址)，len (长度)
    arg_uint64(0, &start);
    arg_uint32(1, &len);
    
    // 确保映射长度是页大小的整数倍
    if (len % PGSIZE != 0) {
        return -1;  // 如果不是整数倍，返回-1
    }

    uint32 pages = len / PGSIZE;  // 计算所需页数
    struct proc *p = myproc();  // 获取当前进程

    // 如果start为0，选择一个空闲的区域进行映射
    if (start == 0) {
        mmap_region_t *m = p->mmap;
        while (m && m->npages < pages) {
            m = m->next;  // 找到合适的区域
        }
        if (m) {
            uvm_mmap(m->begin, pages, PTE_U | PTE_W | PTE_R);  // 映射该区域
        } else {
            return -1;  // 没有合适的区域
        }
    } else {

        uvm_mmap(start, pages, PTE_U | PTE_W | PTE_R);  // 使用给定的地址映射内存
    }


    return start;  // 返回映射的起始地址
}

// 取消内存映射
// uint64 start 起始地址
// uint32 len   范围(字节, 检查是否是page-aligned)
// 成功返回0 失败返回-1
uint64 sys_munmap()
{
    uint64 start;
    uint32 len;

    // 使用 arg_uint64 和 arg_uint32 来读取参数
    arg_uint64(0, &start);
    arg_uint32(1, &len);

    // 确保长度对齐到页面大小
    if (len % PGSIZE != 0) {
        return -1;
    }

    // 调用 uvm_munmap 函数完成解除映射
    uvm_munmap(start, len / PGSIZE);

    return 0;
}

// uint64 sys_print()
// {
//     char str[80*24];
//     arg_str(0, str, 80*24);
//     printf("%s",str);
//     return 0;
// }

// 进程复制
uint64 sys_fork()
{
    return proc_fork();  // 调用 `proc_fork` 创建子进程
}

// 进程等待
// uint64 addr  子进程退出时的exit_state需要放到这里 
uint64 sys_wait()
{
    uint64 exit_addr;

    arg_uint64(0, &exit_addr);
    //assert((void *)exit_addr!=NULL,"wait: exit state is null");
    return proc_wait(exit_addr);
}

// 进程退出
// int exit_state
uint64 sys_exit()
{
    uint32 exit_state;
    arg_uint32(0,&exit_state);

    proc_exit(exit_state);
    return (uint64)exit_state;
}

extern timer_t sys_timer;

// 进程睡眠一段时间
// uint32 second 睡眠时间
// 成功返回0, 失败返回-1
uint64 sys_sleep()
{
    uint32 sleep_duration;  // 睡眠时间（秒）
    uint32 start_ticks;     // 当前的时钟滴答数

    // 获取用户传递的睡眠时间
    arg_uint32(0, &sleep_duration);

    // 获取系统时钟锁，确保时钟更新期间不会被其他进程打扰
    spinlock_acquire(&sys_timer.lk);

    // 记录当前的时钟滴答数
    start_ticks = sys_timer.ticks;

    // 循环检查，直到睡眠时间到达
    while (sys_timer.ticks - start_ticks < sleep_duration * 100) { 
        // 睡眠：当前进程放弃 CPU，等待时钟中断唤醒
        proc_sleep(&sys_timer.ticks, &sys_timer.lk);
    }

    // 释放时钟锁
    spinlock_release(&sys_timer.lk);

    return 0;
}

// 执行一个ELF文件
// char* path
// char** argv
// 成功返回argc 失败返回-1
uint64 sys_exec()
{
    char path[DIR_PATH_LEN];    // 文件路径
    char* argv[ELF_MAXARGS];    // 参数指针数组
    uint64 uargv;                 // 用户态的 argv 地址
    uint64 uarg;                  // 用户态的单个参数地址

    // 获取第一个参数：文件路径
    arg_str(0, path, DIR_PATH_LEN);

    // 获取第二个参数：argv 的地址
    arg_uint64(1, &uargv);

    // 初始化 argv
    memset(argv, 0, sizeof(argv));

    // 遍历用户态 argv 并复制到内核态
    for (int i = 0; i < ELF_MAXARGS; i++) {
        // 从用户态地址读取 argv[i]
        uvm_copyin(myproc()->pgtbl, (uint64)&uarg, uargv + sizeof(uint64) * i, sizeof(uint64));

        if (uarg == 0) {  // 如果读取到 NULL，说明参数列表结束
            argv[i] = 0;
            break;
        }

        // 为参数分配内存
        argv[i] = (char*)pmem_alloc(false);
        if (argv[i] == NULL) {  // 分配失败
            goto bad;
        }

        // 从用户态拷贝字符串到内核态
        uvm_copyin_str(myproc()->pgtbl, (uint64)argv[i], uarg, PGSIZE);

    }

    // 调用 `proc_exec` 执行新的程序
    int ret = proc_exec(path, argv);

    // 执行完成后释放内核态分配的参数内存
    for (int i = 0; i < ELF_MAXARGS && argv[i] != NULL; i++)
        pmem_free((uint64)argv[i], false);
    
    return ret;

bad:
    // 出现错误，释放已分配的内存
    for (int i = 0; i < ELF_MAXARGS && argv[i] != NULL; i++)
        pmem_free((uint64)argv[i], false);

    return -1;

}

uint64 sys_ps()
{
    return myproc()->pid;
}

uint64 sys_psp()
{
    return myproc()->parent->pid;
}

uint64 sys_getcwd()
{
    inode_t* d=myproc()->cwd;
    inode_lock(d);
    dir_print(d);
    inode_unlock(d);
    return 0;
}

